package org.richin.io.util;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.io.FileNotFoundException;
import java.util.Iterator;

/**
 * @author huascar.sanchez@gmail.com (Huascar A. Sanchez)
 */
public class IterableFileReader implements Iterable <String> {
    private BufferedReader reader;

    /**
     * construct a new {@code iterable file reader} object.
     * @param is
     *      file's {@code inputStream}
     * @param encoding
     *      i.e, UTF-8
     */
    public IterableFileReader(InputStream is, String encoding) {
        reader = null;
        try {
            reader = new BufferedReader(new InputStreamReader(is, encoding));
        } catch (UnsupportedEncodingException e) {
            unableToOpenFile(e);
        }
    }

    /**
     * construct a new {@code iterable file reader} object.
     * @param file
     *      file to be read via Java's for-each.
     */
    public IterableFileReader(File file) {
        this(returnOrThrowFileInputStream(file), "UTF-8");
    }
    public Iterator<String> iterator() {
        return new FileIterator(reader);
    }

    private static FileInputStream returnOrThrowFileInputStream(File file) {
        try {
            return new FileInputStream(file);
        } catch (FileNotFoundException e) {
            unableToOpenFile(e);
        }

        throw new Error("unexpected terminal error, please check the file's path");
    }


    private static void unableToOpenFile(Throwable cause){
        throw new RuntimeException("unable to open the given file", cause);
    }


    private static void unableToCloseBufferedReader(Throwable cause){
        throw new RuntimeException("unable to close buffered reader", cause);
    }


    private static void unableToReadFileLine(Throwable cause){
        throw new IllegalStateException("unable to read file's line", cause);
    }

    private static void unsupportedOperation(){
        throw new UnsupportedOperationException("remove is not supported. sorry dude!");
    }

    /**
     *  File's iterator, which is the class that makes the trick for reading a file via the for-each.
     */
    private static class FileIterator implements Iterator <String> {
        private final       BufferedReader  reader;
        private volatile    String          line;

        FileIterator(BufferedReader reader){
            this.reader = reader;
        }

        
        public boolean hasNext() {
            boolean result = false;
           try {
               line = reader.readLine();
               result = line != null;
           } catch (IOException e) {
               unableToReadFileLine(e);
           } finally {
               try {
                  if(!result){
                      reader.close();
                  }
               } catch(IOException e){
                   unableToCloseBufferedReader(e);
               }
           }
           return result;
        }

        
        public String next() {
            return line;
        }

       
        public void remove() {
            unsupportedOperation();
        }
    }

}
